using System;
using System.Xml;
using System.Globalization;


namespace Waymex.Gps
{
	/// <summary>
	/// This object forms part of the AlmanacCollection object which is returned by 
	/// the GetAlmanacs method of the Device object. One object exists for each 
	/// Satellite that exists in the connected GPS devices Almanac.
	/// </summary>
	/// <remarks>
    /// <para><b>Magellan Devices</b></para>
    /// <para>The following Properties are used by bthe Magellan devices.</para>
	/// <list type="table">
	/// <listheader><term>Property</term><description>Type</description></listheader>
	/// <item><term>SatelliteId</term><description>Int32</description></item>
	/// <item><term>WeekNumber</term><description>Int32</description></item>
	/// <item><term>Eccentricity</term><description>Single</description></item>
	/// <item><term>Health</term><description>Int32</description></item>
    /// <item><term>ReferenceTime</term><description>Single</description></item>
	/// <item><term>Inclination</term><description>Single</description></item>
	/// <item><term>RightAscensionRate</term><description>Single</description></item>
	/// <item><term>SqrRootSemiMajorAxis</term><description>Single</description></item>
	/// <item><term>Perigee</term><description>Single</description></item>
	/// <item><term>MeanAnomaly</term><description>Single</description></item>
    /// <item><term>ClockCorrectionAF0</term><description>Single</description></item>
	/// <item><term>ClockCorrectionAF1</term><description>Single</description></item>
	/// </list>
	/// <br/><br/>
    /// <para><b>Garmin Devices</b></para>
	/// <para>
	/// In most cases not all of the Properties of the Almanac object are used. The Properties
	/// actually used are determined by the protocol employed by the connected GPS Device.
	/// Below is a list of which Properties are supported by Protocol. Please note that even though a
	/// Property is identified as being supported does not mean that it will actually be used/populated.
	/// </para>
	/// <br/><br/>
    /// <para>
	/// <b>Protocol D500</b>
	/// </para>
	/// <list type="table">
	/// <listheader><term>Property</term><description>Type</description></listheader>
	/// <item><term>WeekNumber</term><description>Int32</description></item>
	/// <item><term>ReferenceTime</term><description>Single</description></item>
	/// <item><term>ClockCorrectionAF0</term><description>Single</description></item>
	/// <item><term>ClockCorrectionAF1</term><description>Single</description></item>
	/// <item><term>Eccentricity</term><description>Single</description></item>
	/// <item><term>SqrRootSemiMajorAxis</term><description>Single</description></item>
	/// <item><term>MeanAnomaly</term><description>Single</description></item>
	/// <item><term>Perigee</term><description>Single</description></item>
	/// <item><term>RightAscension</term><description>Single</description></item>
	/// <item><term>RightAscensionRate</term><description>Single</description></item>
	/// <item><term>Inclination</term><description>Single</description></item>
	/// </list>
	/// <br/><br/>
	/// <para>
	/// <b>Protocol D501</b>
	/// </para>
	/// <list type="table">
	/// <listheader><term>Property</term><description>Type</description></listheader>
	/// <item><term>WeekNumber</term><description>Int32</description></item>
	/// <item><term>ReferenceTime</term><description>Single</description></item>
	/// <item><term>ClockCorrectionAF0</term><description>Single</description></item>
	/// <item><term>ClockCorrectionAF1</term><description>Single</description></item>
	/// <item><term>Eccentricity</term><description>Single</description></item>
	/// <item><term>SqrRootSemiMajorAxis</term><description>Single</description></item>
	/// <item><term>MeanAnomaly</term><description>Single</description></item>
	/// <item><term>Perigee</term><description>Single</description></item>
	/// <item><term>RightAscension</term><description>Single</description></item>
	/// <item><term>RightAscensionRate</term><description>Single</description></item>
	/// <item><term>Inclination</term><description>Single</description></item>
	/// <item><term>Health</term><description>Int32</description></item>
	/// </list>
	/// <br/><br/>
	/// <para>
	/// <b>Protocol D550</b>
	/// </para>
	/// <list type="table">
	/// <listheader><term>Property</term><description>Type</description></listheader>
	/// <item><term>WeekNumber</term><description>Int32</description></item>
	/// <item><term>ReferenceTime</term><description>Single</description></item>
	/// <item><term>ClockCorrectionAF0</term><description>Single</description></item>
	/// <item><term>ClockCorrectionAF1</term><description>Single</description></item>
	/// <item><term>Eccentricity</term><description>Single</description></item>
	/// <item><term>SqrRootSemiMajorAxis</term><description>Single</description></item>
	/// <item><term>MeanAnomaly</term><description>Single</description></item>
	/// <item><term>Perigee</term><description>Single</description></item>
	/// <item><term>RightAscension</term><description>Single</description></item>
	/// <item><term>RightAscensionRate</term><description>Single</description></item>
	/// <item><term>Inclination</term><description>Single</description></item>
	/// <item><term>SatelliteId</term><description>Int32</description></item>
	/// </list>	
	/// <para>
	/// The 'SatelliteId' Property identifies a satellite in the GPS constellation. 
	/// PRN-01 through PRN-32 are indicated by values of to 0 to 31 respectively 
	/// e.g. 0 = PRN-01, 1 = PRN-02 etc.
	/// </para>
	/// <br/><br/>
	/// <para>
	/// <b>Protocol D551</b>
	/// </para>
	/// <list type="table">
	/// <listheader><term>Property</term><description>Type</description></listheader>
	/// <item><term>WeekNumber</term><description>Int32</description></item>
	/// <item><term>ReferenceTime</term><description>Single</description></item>
	/// <item><term>ClockCorrectionAF0</term><description>Single</description></item>
	/// <item><term>ClockCorrectionAF1</term><description>Single</description></item>
	/// <item><term>Eccentricity</term><description>Single</description></item>
	/// <item><term>SqrRootSemiMajorAxis</term><description>Single</description></item>
	/// <item><term>MeanAnomaly</term><description>Single</description></item>
	/// <item><term>Perigee</term><description>Single</description></item>
	/// <item><term>RightAscension</term><description>Single</description></item>
	/// <item><term>RightAscensionRate</term><description>Single</description></item>
	/// <item><term>Inclination</term><description>Single</description></item>
	/// <item><term>Health</term><description>Int32</description></item>
	/// <item><term>SatelliteId</term><description>Int32</description></item>
	/// </list>	
	/// <para>
	/// The 'SatelliteId' Property identifies a satellite in the GPS constellation. 
	/// PRN-01 through PRN-32 are indicated by values of to 0 to 31 respectively 
	/// e.g. 0 = PRN-01, 1 = PRN-02 etc.
	/// </para>	/// </remarks>

	public class Almanac : DataObjectBase
	{
		private int m_hashCode = -1;

        private short m_weekNumber = 0;
        private float m_referenceTime = 0;
        private float m_clockCorrectionAF0 = 0;
        private float m_clockCorrectionAF1 = 0;
        private float m_eccentricity = 0;
        private float m_sqrRootSemiMajorAxis = 0;
        private float m_meanAnomaly = 0;
        private float m_perigee = 0;
        private float m_rightAscension = 0;
        private float m_rightAscensionRate = 0;
        private float m_inclination = 0;
        private short m_health = 0;
        private short m_satelliteID = 0;

		/// <summary>
		/// See the Waymex GPS Library Developer Notes for further details.
		/// </summary>
		public short WeekNumber
        {
            get
            {
                return m_weekNumber;
            }
            set
            {
                m_weekNumber = value;
            }
        }

		/// <summary>
		/// See the Waymex GPS Library Developer Notes for further details.
		/// </summary>
		public float ReferenceTime
        {
            get
            {
                return m_referenceTime;
            }
            set
            {
                m_referenceTime = value;
            }
        }

		/// <summary>
		/// See the Waymex GPS Library Developer Notes for further details.
		/// </summary>
		public float ClockCorrectionAF0
        {
            get
            {
                return m_clockCorrectionAF0;
            }
            set
            {
                m_clockCorrectionAF0 = value;
            }
        }

		/// <summary>
		/// See the Waymex GPS Library Developer Notes for further details.
		/// </summary>
		public float ClockCorrectionAF1
        {
            get
            {
                return m_clockCorrectionAF1;
            }
            set
            {
                m_clockCorrectionAF1 = value;
            }
        }

		/// <summary>
		/// See the Waymex GPS Library Developer Notes for further details.
		/// </summary>
		public float Eccentricity
        {
            get
            {
                return m_eccentricity;
            }
            set
            {
                m_eccentricity = value;
            }
        }

		/// <summary>
		/// See the Waymex GPS Library Developer Notes for further details.
		/// </summary>
        public float SqrRootSemiMajorAxis
        {
            get
            {
                return m_sqrRootSemiMajorAxis;
            }
            set
            {
                m_sqrRootSemiMajorAxis = value;
            }
        }

		/// <summary>
		/// See the Waymex GPS Library Developer Notes for further details.
		/// </summary>
		public float MeanAnomaly
        {
            get
            {
                return m_meanAnomaly;
            }
            set
            {
                m_meanAnomaly = value;
            }
        }

		/// <summary>
		/// See the Waymex GPS Library Developer Notes for further details.
		/// </summary>
		public float Perigee
        {
            get
            {
                return m_perigee;
            }
            set
            {
                m_perigee = value;
            }
        }

		/// <summary>
		/// See the Waymex GPS Library Developer Notes for further details.
		/// </summary>
		public float RightAscension
        {
            get
            {
                return m_rightAscension;
            }
            set
            {
                m_rightAscension = value;
            }
        }

		/// <summary>
		/// See the Waymex GPS Library Developer Notes for further details.
		/// </summary>
		public float RightAscensionRate
        {
            get
            {
                return m_rightAscensionRate;
            }
            set
            {
                m_rightAscensionRate = value;
            }
        }

		/// <summary>
		/// See the Waymex GPS Library Developer Notes for further details.
		/// </summary>
		public float Inclination
        {
            get
            {
                return m_inclination;
            }
            set
            {
                m_inclination = value;
            }
        }

		/// <summary>
		/// See the Waymex GPS Library Developer Notes for further details.
		/// </summary>
		public short Health
        {
            get
            {
                return m_health;
            }
            set
            {
                m_health = value;
            }
        }

		/// <summary>
		/// See the Waymex GPS Library Developer Notes for further details.
		/// </summary>
		public short SatelliteId
        {
            get
            {
                return m_satelliteID;
            }
            set
            {
                m_satelliteID = value;
            }
        }


		private const string XML_ROOT = "gpsalmanac";
		private const string XML_WEEK_NUMBER = "weeknumber";
		private const string XML_REFERENCE_TIME = "referencetime";
		private const string XML_CLOCK_CORRECTION_AFO = "clockcorrectionaf0";
		private const string XML_CLOCK_CORRECTION_AF1 = "clockcorrectionaf1";
		private const string XML_ECCENTRICITY = "eccentricity";
		private const string XML_SQRROOT_SMAXIS = "sqrrootsmaxis";
		private const string XML_MEAN_ANOMOLY = "meananomoly";
		private const string XML_PERIGREE = "perigree";
		private const string XML_RIGHT_ASCENSION = "rightascension";
		private const string XML_RIGHT_ASCENSION_RATE = "rightascensionrate";
		private const string XML_INCLINATION = "inclination";
		private const string XML_HEALTH = "health";
		private const string XML_SATELLITEID = "satelliteid";

		/// <summary>
		/// Returns an XML representation of the object.
		/// </summary>
		public string ToXml()
		{
			XmlDocument objXMLDOM = new XmlDocument();

			XmlElement objRootNode = null;
			
			try
			{
				//create the root node
				objRootNode = objXMLDOM.CreateElement(XML_ROOT);
			
				//append the root node to the document
				objXMLDOM.AppendChild(objRootNode);

				//create the child nodes
				AddXMLNode(objRootNode, XML_WEEK_NUMBER, WeekNumber.ToString(CultureInfo.InvariantCulture), false);
				AddXMLNode(objRootNode, XML_REFERENCE_TIME, ReferenceTime.ToString(CultureInfo.InvariantCulture), false);
				AddXMLNode(objRootNode, XML_CLOCK_CORRECTION_AFO, ClockCorrectionAF0.ToString(CultureInfo.InvariantCulture), false);
				AddXMLNode(objRootNode, XML_CLOCK_CORRECTION_AF1, ClockCorrectionAF1.ToString(CultureInfo.InvariantCulture), false);
				AddXMLNode(objRootNode, XML_ECCENTRICITY, Eccentricity.ToString(CultureInfo.InvariantCulture), false);
				AddXMLNode(objRootNode, XML_SQRROOT_SMAXIS, SqrRootSemiMajorAxis.ToString(CultureInfo.InvariantCulture), false);
				AddXMLNode(objRootNode, XML_MEAN_ANOMOLY, MeanAnomaly.ToString(CultureInfo.InvariantCulture), false);
				AddXMLNode(objRootNode, XML_PERIGREE, Perigee.ToString(CultureInfo.InvariantCulture), false);
				AddXMLNode(objRootNode, XML_RIGHT_ASCENSION, RightAscension.ToString(CultureInfo.InvariantCulture), false);
				AddXMLNode(objRootNode, XML_RIGHT_ASCENSION_RATE, RightAscensionRate.ToString(CultureInfo.InvariantCulture), false);
				AddXMLNode(objRootNode, XML_INCLINATION, Inclination.ToString(CultureInfo.InvariantCulture), false);
				AddXMLNode(objRootNode, XML_HEALTH, Health.ToString(CultureInfo.InvariantCulture), false);
				AddXMLNode(objRootNode, XML_SATELLITEID, SatelliteId.ToString(CultureInfo.InvariantCulture), false);

			}

            catch (NullReferenceException e)
            {
				throw new XmlException(e.Message, e);
			}

			return objXMLDOM.OuterXml;
		}

		/// <summary>
		/// This method populates the object from XML.
		/// </summary>
        public void XmlLoad(string xml)
		{
			string strXPathRoot = "";
			XmlDocument objDOM = new XmlDocument();

			try
			{
                objDOM.LoadXml(xml);

				if(objDOM.FirstChild.Name == XML_ROOT)
				{
					strXPathRoot = string.Concat("//" , XML_ROOT , "/");

					//need to ignore type conversion errors and missing xml elements
					WeekNumber = ReadXMLNodeAsShort(objDOM, string.Concat(strXPathRoot, XML_WEEK_NUMBER));
					ReferenceTime = ReadXMLNodeAsSingle(objDOM, string.Concat(strXPathRoot, XML_REFERENCE_TIME));
					ClockCorrectionAF0 = ReadXMLNodeAsSingle(objDOM, string.Concat(strXPathRoot, XML_CLOCK_CORRECTION_AFO));
					ClockCorrectionAF1 = ReadXMLNodeAsSingle(objDOM, string.Concat(strXPathRoot, XML_CLOCK_CORRECTION_AF1));
					Eccentricity = ReadXMLNodeAsSingle(objDOM, string.Concat(strXPathRoot, XML_ECCENTRICITY));
					SqrRootSemiMajorAxis = ReadXMLNodeAsSingle(objDOM, string.Concat(strXPathRoot, XML_SQRROOT_SMAXIS));
					MeanAnomaly = ReadXMLNodeAsSingle(objDOM, string.Concat(strXPathRoot, XML_MEAN_ANOMOLY));
					Perigee = ReadXMLNodeAsSingle(objDOM, string.Concat(strXPathRoot, XML_PERIGREE));
					RightAscension = ReadXMLNodeAsSingle(objDOM, string.Concat(strXPathRoot, XML_RIGHT_ASCENSION));
					RightAscensionRate = ReadXMLNodeAsSingle(objDOM, string.Concat(strXPathRoot, XML_RIGHT_ASCENSION_RATE));
					Inclination = ReadXMLNodeAsSingle(objDOM, string.Concat(strXPathRoot, XML_INCLINATION));
					Health = ReadXMLNodeAsShort(objDOM, string.Concat(strXPathRoot, XML_HEALTH));
					SatelliteId = ReadXMLNodeAsShort(objDOM, string.Concat(strXPathRoot, XML_SATELLITEID));			
				}
			}
            catch (NullReferenceException e)
            {
				throw new XmlException(e.Message ,e);
			}

		}
        /// <summary>
        /// Overridden method. Returns true of the values of each
        /// of the properties are equal in value.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns>boolean</returns>
		public override bool Equals(object obj)
		{
			try
			{
				//check the type first
				if(obj.GetType() != this.GetType() )
					return false;

				//cast it
				Almanac objAlmanac = (Almanac)obj;

				if( objAlmanac.ClockCorrectionAF0 != ClockCorrectionAF0 ) return false; 
				if( objAlmanac.ClockCorrectionAF1 != ClockCorrectionAF1 ) return false; 
				if( objAlmanac.Eccentricity != Eccentricity ) return false; 
				if( objAlmanac.Health != Health ) return false; 
				if( objAlmanac.Inclination != Inclination ) return false; 
				if( objAlmanac.MeanAnomaly != MeanAnomaly ) return false; 
				if( objAlmanac.Perigee != Perigee ) return false; 
				if( objAlmanac.ReferenceTime != ReferenceTime ) return false; 
				if( objAlmanac.RightAscension != RightAscension ) return false; 
				if( objAlmanac.RightAscensionRate != RightAscensionRate ) return false; 
				if( objAlmanac.SatelliteId != SatelliteId ) return false; 
				if( objAlmanac.SqrRootSemiMajorAxis != SqrRootSemiMajorAxis ) return false; 
				if( objAlmanac.WeekNumber != WeekNumber ) return false; 

			}
			catch(NullReferenceException ex)
			{
				return false;
			}
			finally
			{
			}
			//if we get here all must be the same.
			return true;
		}
        /// <summary>
        /// Overridden function. Retrieves a value that indicates the hash code value for the object.
        /// </summary>
        /// <returns></returns>
		public override int GetHashCode()
		{
			//this value will be used for the overriden GetHashCode function and should
			//ensure that two object that are the same return the same Hash Code.
			//the hash code has to be imutable to is stored in a member variable.
			if( m_hashCode <= 0 )
				m_hashCode = ClockCorrectionAF0.GetHashCode() ^ ClockCorrectionAF1.GetHashCode();
	
			return m_hashCode;
		}

	}
}
